﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Utils.MathUtilitys;
using System.Diagnostics;

namespace Utils.Drawing
{
    public class PrimitiveRenderer
    {
        #region Fields

        static PrimitiveRenderer instance;
        const int CIRCLE_PARITCLE_COUNT = 16;

        PrimitiveBatch primitiveBatch;
        Matrix transformationMatrix = Matrix.Identity;
        bool active;

        #endregion

        #region Properties

        public Matrix TransformationMatrix { get { return transformationMatrix; } set { transformationMatrix = value; } }
        public static PrimitiveRenderer DEFAULT 
        {
            get
            {
                if(instance == null)
                {
                    instance = new PrimitiveRenderer();
                }
                return instance;
            }
        }

        #endregion

        #region Initialization

        public PrimitiveRenderer()
        {
        }

        public void Init(GraphicsDevice device)
        {
            primitiveBatch = new PrimitiveBatch(device);
        }

        #endregion

        #region Methods

        public void Begin()
        {
            Debug.Assert(!active, "End() must be called before Begin() can be called again");
            active = true;
        }

        public void Begin(Matrix transformationMatrix)
        {
            this.transformationMatrix = transformationMatrix;
            Begin();
        }

        public void End()
        {
            Debug.Assert(active, "Begin() must be called before End() can be called");
            active = false;

            transformationMatrix = Matrix.Identity;
        }


        public void DrawLine(Vector2 p0, Vector2 p1, Color col)
        {
            primitiveBatch.Begin(PrimitiveType.LineList);

            primitiveBatch.AddVertex(CalculatePos(ref p0), col);
            primitiveBatch.AddVertex(CalculatePos(ref p1), col);

            primitiveBatch.End();
        }

        public void DrawRect(Rectangle rect, Color col)
        {
            DrawRect(
                new Vector2(rect.Left, rect.Top), 
                new Vector2(rect.Right, rect.Top),
                new Vector2(rect.Right, rect.Bottom),
                new Vector2(rect.Left, rect.Bottom),
                col);
        }

        public void DrawRect(FRect rect, Color col)
        {
            DrawRect(
                new Vector2(rect.Left, rect.Top),
                new Vector2(rect.Right, rect.Top),
                new Vector2(rect.Right, rect.Bottom),
                new Vector2(rect.Left, rect.Bottom),
                col);
        }

        public void DrawRect(Vector2 pos, Vector2 size, Color col)
        {
            DrawRect(pos, pos + new Vector2(size.X, 0), pos + size, pos + new Vector2(0, size.Y), col);
        }

        public void DrawRect(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, Color col)
        {
            primitiveBatch.Begin(PrimitiveType.LineList);

            primitiveBatch.AddVertex(CalculatePos(ref p0), col);
            primitiveBatch.AddVertex(CalculatePos(ref p1), col);

            primitiveBatch.AddVertex(CalculatePos(ref p1), col);
            primitiveBatch.AddVertex(CalculatePos(ref p2), col);

            primitiveBatch.AddVertex(CalculatePos(ref p2), col);
            primitiveBatch.AddVertex(CalculatePos(ref p3), col);

            primitiveBatch.AddVertex(CalculatePos(ref p3), col);
            primitiveBatch.AddVertex(CalculatePos(ref p0), col);

            primitiveBatch.End();
        }

        public void DrawCircle(Vector2 p, float r, Color col)
        {
            DrawCircle(p, r, col, CIRCLE_PARITCLE_COUNT);
        }

        public void DrawCircle(Vector2 p, float r, Color col, int edgeCount)
        {
            primitiveBatch.Begin(PrimitiveType.LineList);
            Vector2 lastPos = Vector2.Zero;
            for (int i = 0; i <= edgeCount; i++)
            {
                double rad = (float)i / (float)edgeCount * Math.PI * 2.0f;
                Vector2 pos = new Vector2(p.X + (float)Math.Sin(rad) * r, p.Y + (float)Math.Cos(rad) * r);
                pos = CalculatePos(ref pos);
                if(i>0) 
                {
                    primitiveBatch.AddVertex(lastPos, col);
                    primitiveBatch.AddVertex(pos, col);
                }
                lastPos = pos;
            }
            primitiveBatch.End();
        }

        Vector2 CalculatePos(ref Vector2 pos)
        {
            return Vector2.Transform(pos, transformationMatrix);
        }

        #endregion
    }
}
